package com.nineclock.attendance.service.Impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nineclock.attendance.dto.AttendGroupDTO;
import com.nineclock.attendance.dto.AttendGroupPCExtraConfigDTO;
import com.nineclock.attendance.dto.AttendGroupPartDTO;
import com.nineclock.attendance.enums.AttendEnums;
import com.nineclock.attendance.mapper.AttendGroupMapper;
import com.nineclock.attendance.mapper.AttendGroupPartMapper;
import com.nineclock.attendance.mapper.AttendPunchMapper;
import com.nineclock.attendance.pojo.AttendGroup;
import com.nineclock.attendance.pojo.AttendGroupPart;
import com.nineclock.attendance.service.AttendGroupService;
import com.nineclock.common.entity.PageResult;
import com.nineclock.common.entity.Result;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.filter.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.system.feign.SysCompanyUserFeign;
import org.apache.poi.hssf.record.DVALRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

@Service
@Transactional
public class AttendGroupServiceImpl implements AttendGroupService {
    @Autowired
    private AttendGroupMapper attendGroupMapper;
    @Autowired
    private SysCompanyUserFeign companyUserFeign;
    @Autowired
    private AttendPunchMapper attendPunchMapper;
    @Autowired
    private AttendGroupPartMapper attendGroupPartMapper;
    /**
     * 考勤组：分页查询
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageResult<AttendGroupDTO> queryAttendGroupPage(Integer page, Integer pageSize) {
        // 1. 查询当前企业是否初次使用
        LambdaQueryWrapper<AttendGroup> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AttendGroup::getCompanyId, CurrentUserHolder.get().getCompanyId());
        Integer count = attendGroupMapper.selectCount(wrapper);
        if (count==0){
        //2. 如果初次使用需要新增默认考勤组, 设置企业所有部门员工都采用默认考勤组 (调用系统微服务获取企业所有部门)
            //2.1新增考勤组
            AttendGroup attendGroup = this.buildDefaultAttendGroup();
            attendGroupMapper.insert(attendGroup);
            //2.2 设置该公司所有部门都使用该默认考勤组
            List<Long> deptIdList = companyUserFeign.queryDepartmentIds().getData();
            if (!CollectionUtils.isEmpty(deptIdList)){
                for (Long deptId : deptIdList) {
                    //往考勤表中插入员工数据
                    AttendGroupPart attendGroupPart = new AttendGroupPart();
                    attendGroupPart.setAttendGroupId(attendGroup.getId());
                    attendGroupPart.setObjectType(AttendEnums.ATTEND_OBJECT_TYPE_DEP.value());
                    attendGroupPart.setObjectId(deptId);
                    attendGroupPart.setAttendType(AttendEnums.ATTEND_TYPE_YES.value());
                    attendGroupPartMapper.insert(attendGroupPart);
                }
            }
        }
        // 3. 分页查询企业考勤组列表数据
        Page<AttendGroup> iPage = new Page<>(page, pageSize);
        iPage = attendGroupMapper.selectPage(iPage, wrapper);
        // 4. 遍历列表，获取企业考勤组中参与者数量
          //4.1获取当前页的数据，转为DTO集合
        List<AttendGroup> attendGroupList = iPage.getRecords();
        List<AttendGroupDTO> attendGroupDTOS = BeanHelper.copyWithCollection(attendGroupList, AttendGroupDTO.class);
        for (AttendGroupDTO attendGroupDTO : attendGroupDTOS) {
            //查询考勤组成员，统计参数者数量
            LambdaQueryWrapper<AttendGroupPart> wrapper2 = new LambdaQueryWrapper<>();
            wrapper2.eq(AttendGroupPart::getAttendGroupId,attendGroupDTO.getId());
            wrapper2.eq(AttendGroupPart::getAttendType,AttendEnums.ATTEND_TYPE_YES.value());
            List<AttendGroupPart> attendGroupPartList = attendGroupPartMapper.selectList(wrapper2);
            // 统计参与者直接是员工的数量 + 参与者是部门下的员工数量
            Integer sum = 0; // 参与者总数
            List<Long> deptIdList = new ArrayList<>();
            for (AttendGroupPart groupPart : attendGroupPartList) {
                // 如果参与者是员工，总数 + 1
                if (groupPart.getObjectType().equals(AttendEnums.ATTEND_OBJECT_TYPE_USER.value())){
                    sum++;
                }else {
                    // 如果参与者是部门，统计部门id
                    deptIdList.add(groupPart.getObjectId());
                }
            }
            // 调用系统微服务接口，获取指定部门下的员工数量
            Integer deptUserCount = companyUserFeign.queryUserCountByDepartmentId(deptIdList).getData();
            sum=sum+deptUserCount;// 汇总所有参与者数量
            attendGroupDTO.setMemberNum(sum);
        }
        // 在第2步骤中, 需要根据查询当前企业下部门ID集合，用于将默认考勤组关联到部门。故需要在系统微服务中提供Feign接口逻辑为根据企业ID查询企业部门ID集合。
        //
        // 在第4步骤中, 需要计算考勤组下关联的用户数量，故还需要提供根据部门ID查询部门员工数量的接口。
        return new PageResult<>(iPage.getTotal(),iPage.getPages(),attendGroupDTOS);
    }



    //新增默认考勤组
    private AttendGroup buildDefaultAttendGroup() {
        AttendGroup attendGroup = new AttendGroup();
        attendGroup.setName("默认考勤组");
        attendGroup.setAddressRange(500);//有效距离
        attendGroup.setAllowLateMinutes(10);//允许迟到分钟数
        attendGroup.setLateMinutes(30);//允许矿工分钟数
        attendGroup.setCompanyId(CurrentUserHolder.get().getCompanyId());
        attendGroup.setLng(new BigDecimal("23.13514700000000000000"));//经度
        attendGroup.setLat(new BigDecimal("113.43467700000000000000"));//纬度
        attendGroup.setAddressName("广州黑马程序员");
        attendGroup.setAddress("广东省广州市天河区珠吉街道珠吉路58号黑马培训机构");
        attendGroup.setStartWorkTime("09:00:00");//上班时间
        attendGroup.setOffWorkTime("18:00:00");//下班时间
        attendGroup.setStartNoonRestTime("12:00:00");
        attendGroup.setEndNoonRestTime("14:00:00");
        attendGroup.setWorkdays("1,1,1,1,1,0,0");//工作日
        attendGroup.setCreateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setCreateTime(new Date());
        attendGroup.setUpdateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setUpdateTime(new Date());
        return attendGroup;
    }

    /**
     * 考勤组: 添加
     * @param attendGroupDTO
     */
    @Override
    public void addAttendGroup(AttendGroupDTO attendGroupDTO) {
        //1.健壮性判断
        if (attendGroupDTO == null) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        //2.组装考勤组对象, 保存考勤组 ===> at_attend_group
        AttendGroup attendGroup = BeanHelper.copyProperties(attendGroupDTO, AttendGroup.class);
        //2.1 设置考勤组关联的企业ID - 创建人ID - 更新人ID 等基本信息
        attendGroup.setCompanyId(CurrentUserHolder.get().getCompanyId());
        attendGroup.setCreateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setUpdateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setCreateTime(new Date());
        attendGroup.setUpdateTime(new Date());
        //2.2 设置工作日，使用逗号 "," 分割
        if (attendGroupDTO.getWorkdays() != null) {
            attendGroup.setWorkdays(StrUtil.join(",", attendGroupDTO.getWorkdays().toArray()));
        }
        //2.3 设置必须/不用打卡的日期，获取出来之后使用逗
        List<AttendGroupPCExtraConfigDTO> extraConfig = attendGroupDTO.getExtraConfig();
        if (!CollectionUtils.isEmpty(extraConfig)) {
            List<String> nesessaryList = new ArrayList<>();//必须打卡的日期
            List<String> unnesessaryList = new ArrayList<>();//无需打卡的日期
            for (AttendGroupPCExtraConfigDTO configDTO : extraConfig) {
                //日期格式化
                String time = configDTO.getSetDate();
                String date = DateTimeUtil.dateToStr(new Date(Long.valueOf(time)), DateTimeUtil.TIME_FORMAT_2);
                if (configDTO.getRequiredAttend() == 1) {
                    nesessaryList.add(date);
                } else {
                    unnesessaryList.add(date);
                }
            }
            // 设置必须打卡的日期，无需打卡的日趋","分割
            attendGroup.setNecessaryTimeList(StrUtil.join(",", unnesessaryList.toArray()));
            attendGroup.setUnnecessaryTimeList(StrUtil.join(",", unnesessaryList.toArray()));
        }
            //2.4 保存考勤组
            attendGroupMapper.insert(attendGroup);
            saveAttendGroupPart(attendGroupDTO, attendGroup.getId());
    }
    /**
     * 保存考勤组
     * @param attendGroupDTO
     * @param attendGroupId
     */
    //3.保存关联用户、部门到该考勤组 ===> at_attend_group_part
    private void saveAttendGroupPart(AttendGroupDTO attendGroupDTO, Long attendGroupId) {
        //3.1 需要参与考勤组的对象处理
        List<AttendGroupPartDTO> participates = attendGroupDTO.getParticipates();
        participates.forEach(attendGroupPartDTO -> {
            //3.1.1 判断当前对象是否加入到某个考勤组（要求: 用户/部门只能加入一个考勤组，如果设置新考勤组，需要先删除旧的考勤组）
            LambdaQueryWrapper<AttendGroupPart> warpper = new LambdaQueryWrapper<>();
            warpper.eq(AttendGroupPart::getObjectId, attendGroupPartDTO.getObjectId());
            warpper.eq(AttendGroupPart::getObjectType, attendGroupPartDTO.getObjectType());
            warpper.eq(AttendGroupPart::getAttendType, AttendEnums.ATTEND_TYPE_YES.value());
            Integer count = attendGroupPartMapper.selectCount(warpper);
            if (count > 0) {
                //3.1.2 如果存在旧的考勤组, 需要删除旧考勤组
                attendGroupPartMapper.delete(warpper);
            }
            //3.1.3 给考勤对象关联新的考勤组
            AttendGroupPart attendGroupPart = new AttendGroupPart();
            attendGroupPart.setAttendGroupId(attendGroupId);
            attendGroupPart.setAttendType(AttendEnums.ATTEND_TYPE_YES.value());
            attendGroupPart.setObjectId(attendGroupPartDTO.getObjectId());
            attendGroupPart.setObjectType(attendGroupPartDTO.getObjectType());
        });
        //3.2 不需要参与考勤组的对象处理
        List<AttendGroupPartDTO> notParticipates = attendGroupDTO.getNotParticipates();
        //3.2.1 判断用户/部门是否已经在无需参与考勤组中
        notParticipates.forEach(attendGroupPartDTO -> {
            LambdaQueryWrapper<AttendGroupPart> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(AttendGroupPart::getObjectId, attendGroupPartDTO.getObjectId());
            wrapper.eq(AttendGroupPart::getObjectType, attendGroupPartDTO.getObjectType());
            wrapper.eq(AttendGroupPart::getAttendType, AttendEnums.ATTEND_TYPE_NO.value());
            //3.2.2 判定是否已经存在，如果不存在，则新增无需考勤组记录，如果存在，不需要处理
            Integer count = attendGroupPartMapper.selectCount(wrapper);
            if (count == 0) {
                AttendGroupPart attendGroupPart = new AttendGroupPart();
                attendGroupPart.setAttendType(AttendEnums.ATTEND_TYPE_NO.value());
                attendGroupPart.setAttendGroupId(attendGroupId);
                attendGroupPart.setObjectId(attendGroupPartDTO.getObjectId());
                attendGroupPart.setObjectType(attendGroupPartDTO.getObjectType());
                //新增无需参加考勤的用户
                attendGroupPartMapper.insert(attendGroupPart);
            }
        });
    }
    /**
     * 考勤组: 获取当前登录用户考勤组
     * @return
     */
    @Override
    public AttendGroupDTO getAttendGroupByUserId() {
        //获取登入者员工id
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();
        //根据id查询考勤组参与者
        LambdaQueryWrapper<AttendGroupPart> warpper = new LambdaQueryWrapper<>();
        warpper.eq(AttendGroupPart::getObjectId,companyUserId);
        warpper.eq(AttendGroupPart::getObjectType,AttendEnums.ATTEND_OBJECT_TYPE_USER.value());
        AttendGroupPart attendGroupPart = attendGroupPartMapper.selectOne(warpper);
        if (attendGroupPart==null){
            //如果不存在则查询员工上级部门id集合，参训考勤参与者
           List<Long> listResult = companyUserFeign.queryDepartmentsByUserId(companyUserId).getData();
            for (Long aLong : listResult) {
                LambdaQueryWrapper<AttendGroupPart> wrapperDept = new LambdaQueryWrapper<>();
                wrapperDept.eq(AttendGroupPart::getObjectId,aLong);
                wrapperDept.eq(AttendGroupPart::getObjectType,AttendEnums.ATTEND_OBJECT_TYPE_DEP.value());
                wrapperDept.eq(AttendGroupPart::getAttendType,AttendEnums.ATTEND_TYPE_YES.value());
                attendGroupPart = attendGroupPartMapper.selectOne(wrapperDept);
                //如果参与者信息存在，则跳出循环
                if (attendGroupPart!=null){
                    break;
                }
            }
        }
        //如果不存在，则直接抛异常
        if(attendGroupPart==null){
            throw new NcException(ResponseEnum.USER_NOT_MATCH_ATTENDGROUP);
        }
        //根据考勤组id查询考勤组
        AttendGroup attendGroup = attendGroupMapper.selectById(attendGroupPart.getAttendGroupId());
        //数据转换
        AttendGroupDTO attendGroupDTO = BeanHelper.copyProperties(attendGroup, AttendGroupDTO.class);
        //额外设置工作日属性
        String workdays = attendGroup.getWorkdays();
        attendGroupDTO.setWorkdays(Arrays.asList(workdays.split(",")));
        return attendGroupDTO;
    }
}
